﻿using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Reader.Utils;

namespace Reader.Controllers;

public class BookController  : Controller
{
    private readonly ApplicationDbContext _context;
    private readonly IWebHostEnvironment _environment;
    private readonly SessionService _sessionService;

    public BookController(ApplicationDbContext context, IWebHostEnvironment environment, SessionService sessionService)
    {
        _context = context;
        _environment = environment;
        _sessionService = sessionService;
    }
    
    [HttpGet]
    public IActionResult UploadBook()
    {
        if (!HttpContext.IsLogin())
            return RedirectToAction("Login", "User");
        // 分类
        var categories = _context.Categories.ToList();
        ViewData["categories"] = categories;
        return View();
    }
    
    
    [HttpPost]
    public IActionResult UploadBook([FromForm] BookUploadModel model)
    {
        if (!HttpContext.IsLogin())
            return RedirectToAction("Login", "User");
        
        ModelState.Remove("CoverImage");
        if (!ModelState.IsValid)
        {
            var categories = _context.Categories.ToList();
            ViewData["categories"] = categories;
            return View();
        }
        // 保存封面图像
        string coverImagePath = "";
        if (!string.IsNullOrEmpty(model.CoverImage))
        {
            coverImagePath = model.CoverImage;
        }
        else
        {
            var filesPath = Path.Combine(_environment.WebRootPath, "files");
            var uniqueFileName = Guid.NewGuid().ToString() + ".jpg";
            var filePath = Path.Combine(filesPath, uniqueFileName);
            BookHelper.GenerateCover(model.Title, model.Author, filePath);
            coverImagePath = Path.Combine("files", uniqueFileName);
        }
        // 保存书籍文件
        string storagePath = SaveFile(model.StoragePath, "books");
        if (string.IsNullOrEmpty(storagePath))
        {
            ModelState.AddModelError("StoragePath", "书籍文件上传失败。");
            return BadRequest(ModelState);
        }

        // 计算书籍文件的MD5值
        string fileMd5 = HashGenerator.CalculateMd5(Path.Combine(_environment.WebRootPath, storagePath));

        // 检查数据库中是否已存在相同的MD5值
        if (_context.Books.Any(b => b.MD5 == fileMd5))
        {
            ModelState.AddModelError("MD5", "该文件已存在于数据库中。");
            return BadRequest(ModelState);
        }

        // 创建新的书籍对象
        var book = new Book
        {
            Title = model.Title,
            Author = model.Author,
            Description = model.Description,
            CoverImage = coverImagePath,
            Type = model.Type,
            UploadedUserId = _sessionService.GetInt32("user.id"),
            StoragePath = storagePath,
            MD5 = fileMd5,
            UploadTime = DateTime.UtcNow,
            Status = model.Status,
            CategoryId = model.Category
        };
        
        // 将书籍保存到数据库
        _context.Books.Add(book);
        _context.SaveChanges();
        
        if (model.Type == Book.BookType.TXT)
        {
            // txt 文本需要解析章节
            var bookChapters = BookHelper.ParseChaptersWithContent(Path.Combine(_environment.WebRootPath, storagePath));
            int loopNum = 0;
            foreach (var chapter in bookChapters)
            {
                _context.Chapters.Add(new Chapter()
                {
                    BookId = book.Id,
                    ChapterName = chapter.Title,
                    ChapterNumber = loopNum++,
                    Content = chapter.Content,
                    UploadTime = DateTime.UtcNow,
                });
            }
            _context.SaveChanges();
        }
        return RedirectToAction("BookReader", new {bookId = book.Id});
    }
    
    private string SaveFile(IFormFile file, string folderName)
    {
        if (file == null || file.Length == 0)
        {
            return null;
        }

        string uploadsFolder = Path.Combine(_environment.WebRootPath, folderName);
        if (!Directory.Exists(uploadsFolder))
        {
            Directory.CreateDirectory(uploadsFolder);
        }

        string uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
        string filePath = Path.Combine(uploadsFolder, uniqueFileName);

        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            file.CopyTo(stream);
        }

        return Path.Combine(folderName, uniqueFileName);
    }
    
    
    [HttpGet]
    public IActionResult BookReader(int bookId)
    {
        Console.WriteLine(bookId);
        var book = _context.Books.Include(b => b.Chapters).Include(b => b.UploadedBy).FirstOrDefault(e => e.Id == bookId);
        if (book == null)
        {
            return NotFound();
        }
        
        if (book.Type == Book.BookType.PDF)
        {
            UpdateBookReadingCount(book);
            if (_sessionService.IsLogin())
            {
                int readPos = GetUserReadPos(book.Id);
                ViewData["read_pos"] = readPos == -1 ? 1 : readPos;
                if(readPos == -1)
                    SetUserReadPos(book.Id, 1);
            }
            return View("BookReaderPdf", book);
        }
        else
        {
            if (_sessionService.IsLogin())
            {
                int readPos = GetUserReadPos(book.Id);
                if (readPos != -1)
                {
                    return RedirectToAction("TxtBookContent", "Book", new { chapterId = readPos });
                }
            }
            return View("TxtBookChapter", book);
        }
    }

    public IActionResult RecordPdfPos(int bookId, int pos)
    {
        if (!_sessionService.IsLogin())
        {
            return Ok();
        }
        SetUserReadPos(bookId, pos);
        return Ok();
    }
    
    public IActionResult TxtBookChapter(int bookId)
    {
        var book = _context.Books.Include(b => b.Chapters).Include(b => b.UploadedBy).FirstOrDefault(e => e.Id == bookId);
        if (book == null)
        {
            return NotFound();
        }
        return View("TxtBookChapter", book);
    }
    
    
    [HttpGet]
    public IActionResult TxtBookContent(int chapterId)
    {
        var chapter = _context.Chapters.FirstOrDefault(e => e.ChapterId == chapterId);
        if (chapter == null)
            return NotFound();
        var book = _context.Books.First(e => e.Id == chapter.BookId);
        ViewData["book"] = book;
        
        if (chapter.ChapterNumber > 0)
        {
            ViewData["last_chapter"] = _context.Chapters.First(e => e.BookId == chapter.BookId && e.ChapterNumber == chapter.ChapterNumber - 1);
        }
        else
        {
            ViewData["last_chapter"] = null;
        }

        if (chapter.ChapterNumber != _context.Chapters.Count(e => e.BookId == chapter.BookId) - 1)
        {
            ViewData["next_chapter"] = _context.Chapters.First(e => e.BookId == chapter.BookId && e.ChapterNumber == chapter.ChapterNumber + 1);
        }
        else
        {
            ViewData["next_chapter"] = null;
        }
        UpdateBookReadingCount(book);
        if (_sessionService.IsLogin())
        {
            SetUserReadPos(book.Id, chapterId);
        }
        return View(chapter);
    }

    [HttpGet]
    public IActionResult BookShelf()
    {
        if (!_sessionService.IsLogin())
        {
            return NotFound();
        }
        var readingRecords = _context.ReadingRecords
            .Include(e => e.Book)
            .Include(e => e.Book.UploadedBy)
            .Where(e => e.UserId == _sessionService.GetUserId())
            .OrderByDescending(e => e.UpdateTime)
            .ToList();
        ViewData["books"] = readingRecords;
        return View();
    }
    
    [HttpGet]
    public IActionResult BookSearch(string keyword)
    {
        if (keyword == null) keyword = "";
        keyword = keyword.Trim();
        if (string.IsNullOrEmpty(keyword))
        {
            return View();
        }
        var books = _context.Books.Where(e => EF.Functions.Like(e.Title, $"%{keyword}%") || EF.Functions.Like(e.Author, $"%{keyword}%")).ToList();
        ViewData["books"] = books;
        return View();
    }
    
    [HttpGet]
    public IActionResult EditorBook(int bookId)
    {
        Book book = _context.Books.FirstOrDefault(e => e.Id == bookId);
        if (book == null)
            return NotFound();
        ViewData["book"] = book;
        // 分类
        var categories = _context.Categories.ToList();
        ViewData["categories"] = categories;
        return View("BookEditor");
    }

    [HttpPost]
    public IActionResult EditorBook([FromForm] BookUploadModel model, [FromForm]int bookId)
    {
        Book book = _context.Books.FirstOrDefault(e => e.Id == bookId);
        if (book == null)
            return NotFound();
        book.Type = model.Type;
        book.CoverImage = model.CoverImage;
        book.Title = model.Title;
        book.Author = model.Author;
        book.Description = model.Description;
        book.Status = model.Status;
        _context.SaveChanges();
        if (book.Type == Book.BookType.PDF)
        {
            return RedirectToAction("BookReader", new { bookId = bookId });
        }
        else
        {
            return RedirectToAction("TxtBookChapter", new { bookId = bookId });
        }
    }

    public IActionResult BookCategory(int categoryId)
    {
        var categories = _context.Categories.ToList();
        ViewData["categories"] = categories;
        List<Book> books;
        if (categoryId == 0)
        {
            // 查询全部
            books = _context.Books.ToList();
        }
        else
        {
            books = _context.Books.Where(e => e.CategoryId == categoryId).ToList();
        }
        ViewData["categoryId"] = categoryId;
        return View(books);
    }
    
    
    int GetUserReadPos(int bookId)
    {
        if (!_sessionService.IsLogin())
        {
            return -1;
        }
        var record = _context.ReadingRecords.FirstOrDefault(r => r.BookId == bookId && r.UserId == _sessionService.GetUserId());
        if (record == null)
            return -1;
        return record.ReadPos;
    }

    void SetUserReadPos(int bookId, int pos)
    {
        var record = _context.ReadingRecords.FirstOrDefault(r => r.BookId == bookId && r.UserId == _sessionService.GetUserId());
        if (record == null)
        {
            _context.ReadingRecords.Add(new ReadingRecord()
            {
                BookId = bookId,
                UserId = _sessionService.GetUserId(),
                CreateTime = DateTime.UtcNow,
                UpdateTime = DateTime.UtcNow,
                ReadPos = pos
            });
        }
        else
        {
            record.ReadPos = pos;
            record.UpdateTime = DateTime.Now;
        }
        _context.SaveChanges();
    }
    
    void UpdateBookReadingCount(Book book)
    {
        book.ReadCount += 1;
        _context.SaveChanges();
    }
}

